package znzz.db.util;

import org.bson.Document;
import org.bson.types.ObjectId;
import znzz.core.entity.BaseVO;
import znzz.db.entity.ParamVO;
import znzz.db.util.constant.SyncConst;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * Document转javabean核心类
 * <p/>
 * Created by wangjwm on 2017/7/6.
 */
public class BeanUtil {


    /**
     * 字表document转javabean
     *
     * @param document     主表document
     * @param subDocuments 字表document
     * @param paramVO      paramVO
     * @return
     */
    public static List<BaseVO> subDocument2Bean(Document document, List<Object> subDocuments, ParamVO paramVO) {
        List<BaseVO> list = new ArrayList<BaseVO>();
        for (int i = 0; i < subDocuments.size(); i++) {
            Object subDocument = subDocuments.get(i);
            list.add(document2Bean(document, subDocument, paramVO));
        }
        return list;
    }

    /**
     * document转javabean
     *
     * @param document
     * @param paramVO
     * @param <T>
     * @return
     */
    public static <T extends BaseVO> T document2Bean(Document document, Object subDocument, ParamVO paramVO) {
        // 如果为空返回
        Class clazz = null;
        Object objVO = null;
        // 如果是通用SubBaseVO
        String name = "";
        if (paramVO.isCommonSubVO()) {
            name = "znzz.core.entity.SubBaseVO";
        } else {
            String beanName = (paramVO.getSubTableName() == null) ? paramVO.getTableName() : paramVO.getSubTableName();
            name = "znzz.db.entity." + paramVO.getTableName().toLowerCase() + "." + beanName;
        }
        try {
            clazz = Class.forName(name);
            objVO = clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 获得字段名称
        Field[] fields = getFields(clazz, new ArrayList<Field>());
        Method[] methods = getMethods(clazz, new ArrayList<Method>());
        for (Field field : fields) {
            String varName = field.getName();
            Class<?> varType = field.getType();
            Object fieldValue = null;
            if (subDocument == null) {
                fieldValue = document.get(varName);
            } else {
                // 处理Document类型（类似ExtendEnum）
                if (subDocument instanceof Document) {
                    if (varName == "_subId") {
                        fieldValue = ((Document) subDocument).get("_id");
                        if(fieldValue == null) {
                            fieldValue = new ObjectId().toString();
                        }
                    } else {
                        fieldValue = (SyncConst.parentAttrs.contains(varName)) ? document.get(varName) : ((Document) subDocument).get(varName);
                    }
                }
                // 处理Array类型（默认赋值_subId）
                else {
                    fieldValue = (varName == "_subId") ? subDocument : document.get(varName);
                }
            }
            if (fieldValue != null) {
                String fieldSetName = parSetName(varName);
                if (!checkSetMet(methods, fieldSetName)) {
                    continue;
                }
                try {
                    Method fieldSetMet = clazz.getMethod(fieldSetName, varType);
                    fieldSetMet.invoke(objVO, getFieldVal(field.getType(), fieldValue));
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return (T) objVO;
    }

    /**
     * 递归输出这个类、父类直接Object类的所有private属性
     *
     * @param clazz  clazz
     * @param fields list
     * @return
     */
    private static Field[] getFields(Class clazz, List<Field> fields) {
        String class_name = clazz.getName();
        Field[] obj_fields = clazz.getDeclaredFields();
        for (Field field : obj_fields) {
            field.setAccessible(true);
            fields.add(field);
        }
        if (clazz.getSuperclass() != null) {
            getFields(clazz.getSuperclass(), fields);
        }
        return fields.toArray(new Field[0]);
    }

    /**
     * 递归输出这个类、父类直接Object类的所有set方法
     *
     * @param clazz
     * @param methods
     * @return
     */
    private static Method[] getMethods(Class clazz, List<Method> methods) {
        String class_name = clazz.getName();
        Method[] obj_methods = clazz.getDeclaredMethods();
        for (Method method : obj_methods) {
            method.setAccessible(true);
            methods.add(method);
        }
        if (clazz.getSuperclass() != null) {
            getMethods(clazz.getSuperclass(), methods);
        }
        return methods.toArray(new Method[0]);
    }


    /**
     * 获得字段真实值（用于处理MongoDB 与 SQL数据转换）
     * 这里有几个类型转换写的贼挫，后面再修改吧
     *
     * @param varType    字段类型
     * @param fieldValue 字段值
     * @return
     */
    private static Object getFieldVal(Class<?> varType, Object fieldValue) {
        // 处理时间类型
        if (Timestamp.class.isAssignableFrom(varType)) {
            SimpleDateFormat sfEnd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat sfStart = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
            try {
                String formatVal = sfEnd.format(sfStart.parse(fieldValue.toString()));
                Date timeDate = sfEnd.parse(formatVal);
                Timestamp dateTime = new Timestamp(timeDate.getTime());
                return dateTime;
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        // 防止ObjectId类型异常
        else if (String.class.isAssignableFrom(varType)) {
            return fieldValue.toString();
        } else if (int.class.isAssignableFrom(varType) && ("true".equals(fieldValue.toString()) || "false".equals(fieldValue.toString()))) {
            return ("true".equals(fieldValue.toString())) ? 1 : 0;
        } else if (float.class.isAssignableFrom(varType)) {
            String str = fieldValue.toString();
            Double d = (str.indexOf(".") != -1) ?
                    ((Double) fieldValue).doubleValue()
                    : ((Integer) fieldValue).doubleValue();
            return d.floatValue();
        }
        return fieldValue;
    }

    /**
     * 拼接在某属性的 set方法
     *
     * @param fieldName
     * @return String
     */
    private static String parSetName(String fieldName) {
        if (null == fieldName || "".equals(fieldName)) {
            return null;
        }
        int startIndex = 0;
        /*if (fieldName.charAt(0) == '_')
            startIndex = 1;*/
        return "set"
                + fieldName.substring(startIndex, startIndex + 1).toUpperCase()
                + fieldName.substring(startIndex + 1);
    }

    /**
     * 判断是否存在某属性的 set方法
     *
     * @param methods
     * @param fieldSetMet
     * @return boolean
     */
    private static boolean checkSetMet(Method[] methods, String fieldSetMet) {
        for (Method met : methods) {
            if (fieldSetMet.equals(met.getName())) {
                return true;
            }
        }
        return false;
    }

}
